package net.mooctest;


import org.junit.jupiter.api.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.aggregator.ArgumentsAccessor;
import org.junit.jupiter.params.provider.CsvSource;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import static org.junit.jupiter.api.Assertions.*;

@DisplayName("数组多节点树测试")
class ArrayMultiTreeNodeTest {
    private static ArrayMultiTreeNode<String> treeRoot;

    @BeforeEach
    void setUp() {
        treeRoot = new ArrayMultiTreeNode<>("root");
    }

    @AfterAll
    static void tearDown() {
    }

    @Nested
    @DisplayName("add方法测试")
    class AddTest {

        @ParameterizedTest
        @CsvSource({"level_1"})
        @DisplayName("测试添加非null node")
        void add1(ArgumentsAccessor args) {
            ArrayMultiTreeNode<String> arrayMultiTreeNode = new ArrayMultiTreeNode<>(args.getString(0));
            boolean result = treeRoot.add(arrayMultiTreeNode);
            System.out.println(treeRoot);

            assertTrue(result);
        }

        @Test
        @DisplayName("测试添加  null node")
        void add2() {
            boolean result = treeRoot.add(null);
            System.out.println(treeRoot);

            assertFalse(result);
        }
    }

    @Nested
    @DisplayName("addSubtrees方法测试")
    class AddSubtreesTest {

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2,level_1_3"})
        @DisplayName("测试添加非null node")
        void add1(ArgumentsAccessor args) {
            // 添加测试用例
            List<ArrayMultiTreeNode<String>> arrayMultiTreeNodeCollection = new ArrayList<>();
            for (int i = 0; i < args.size(); i++) {
                arrayMultiTreeNodeCollection.add(new ArrayMultiTreeNode<>(args.getString(i)));
            }

            boolean result = treeRoot.addSubtrees(arrayMultiTreeNodeCollection);
            System.out.println(treeRoot);

            assertTrue(result);
        }

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2,level_1_3"})
        @DisplayName("部分节点为null")
        void add2(ArgumentsAccessor args) {
            // 添加测试用例
            List<ArrayMultiTreeNode<String>> arrayMultiTreeNodeCollection = new ArrayList<>();
            for (int i = 0; i < args.size(); i++) {
                arrayMultiTreeNodeCollection.add(new ArrayMultiTreeNode<>(args.getString(i)));
            }
            arrayMultiTreeNodeCollection.add(new ArrayMultiTreeNode<>(null));

            // TODO:源程序中的漏洞,未检测出添加的部分节点为null
            arrayMultiTreeNodeCollection.add(null);

            boolean result = treeRoot.addSubtrees(arrayMultiTreeNodeCollection);
            System.out.println(treeRoot);

            assertTrue(result);
        }

        @Test
        @DisplayName("参数传入的数组长度为0")
        void add3() {
            // 添加测试用例
            List<ArrayMultiTreeNode<String>> arrayMultiTreeNodeCollection = new ArrayList<>();

            boolean result = treeRoot.addSubtrees(arrayMultiTreeNodeCollection);
            System.out.println(treeRoot);

            assertFalse(result);
        }

        @Test
        @DisplayName("传入参数为null")
        void add4() {
            boolean result = treeRoot.addSubtrees(null);
            System.out.println(treeRoot);

            assertFalse(result);
        }

        @Test
        @DisplayName("全部节点都为null")
        void add5() {
            // 添加测试用例
            List<ArrayMultiTreeNode<String>> arrayMultiTreeNodeCollection = new ArrayList<>();
            for (int i = 0; i < 2; i++) {
                arrayMultiTreeNodeCollection.add(null);
            }

            boolean result = treeRoot.addSubtrees(arrayMultiTreeNodeCollection);
            System.out.println(treeRoot);

            assertFalse(result);
        }
    }

    @Nested
    @DisplayName("clear方法测试")
    class ClearTest {

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2,level_1_3"})
        @DisplayName("子树不为空")
        void clear(ArgumentsAccessor args) {
            // 添加测试用例
            addSubtrees(treeRoot, args);
            System.out.println(treeRoot);
            treeRoot.clear();
            System.out.println(treeRoot);

            assertEquals(0, treeRoot.subtrees().size());
        }

        @Test
        @DisplayName("子树为空")
        void clear1() {
            System.out.println(treeRoot);
            treeRoot.clear();
            System.out.println(treeRoot);

            assertEquals(0, treeRoot.subtrees().size());
        }
    }

    @Nested
    @DisplayName("contains方法测试")
    class ContainsTest {

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2"})
        @DisplayName("查找存在的节点")
        void contains1(ArgumentsAccessor args) {
            List<ArrayMultiTreeNode<String>> arrayMultiTreeNodeList = addSubtrees(treeRoot, args);
            System.out.println(treeRoot);

            assertTrue(treeRoot.contains(arrayMultiTreeNodeList.get(1)));
        }

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2"})
        @DisplayName("查找 null 节点")
        void contains2(ArgumentsAccessor args) {
            addSubtrees(treeRoot, args);
            System.out.println(treeRoot);

            assertFalse(treeRoot.contains(null));
        }

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2"})
        @DisplayName("查找的节点为根节点")
        void contains3(ArgumentsAccessor args) {
            addSubtrees(treeRoot, args);
            System.out.println(treeRoot);

            assertFalse(treeRoot.contains(new ArrayMultiTreeNode<>("root2")));
        }

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2"})
        @DisplayName("当前节点为叶子节点")
        void contains4(ArgumentsAccessor args) {
            List<ArrayMultiTreeNode<String>> arrayMultiTreeNodeList = addSubtrees(treeRoot, args);

            assertFalse(arrayMultiTreeNodeList.get(0).contains(treeRoot));
        }

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2"})
        @DisplayName("在子树的子树中包含该节点")
        void contains5(ArgumentsAccessor args) {
            // 添加一级子树
            List<ArrayMultiTreeNode<String>> arrayMultiTreeNodeList = addSubtrees(treeRoot, args);

            // 添加二级子树
            List<ArrayMultiTreeNode<String>> subArrayMultiTreeNodeList = addSubtrees(arrayMultiTreeNodeList.get(1), args);

            assertTrue(treeRoot.contains(subArrayMultiTreeNodeList.get(0)));
        }

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2"})
        @DisplayName("查找一个非根,非叶,非null,且根节点不包含的节点")
        void contains6(ArgumentsAccessor args) {
            addSubtrees(treeRoot, args);

            ArrayMultiTreeNode<String> newArrayMultiTreeNodeList = getLeafNode(args);

            assertFalse(treeRoot.contains(newArrayMultiTreeNodeList));
        }
    }

    @Nested
    @DisplayName("DropSubtree方法测试")
    class DropSubtreeTest {

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2,level_1_3"})
        @DisplayName("删除存在的节点")
        void dropSubtree1(ArgumentsAccessor args) {
            List<ArrayMultiTreeNode<String>> arrayMultiTreeNodeList = addSubtrees(treeRoot, args);

            boolean result = treeRoot.dropSubtree(arrayMultiTreeNodeList.get(0));

            assertTrue(result);
        }

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2,level_1_3"})
        @DisplayName("删除 null 节点")
        void dropSubtree2(ArgumentsAccessor args) {
            addSubtrees(treeRoot, args);

            boolean result = treeRoot.dropSubtree(null);

            assertFalse(result);
        }

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2,level_1_3"})
        @DisplayName("删除一个根节点")
        void dropSubtree3(ArgumentsAccessor args) {
            addSubtrees(treeRoot, args);
            ArrayMultiTreeNode<String> arrayMultiTreeNode = new ArrayMultiTreeNode<>("newRoot");

            boolean result = treeRoot.dropSubtree(arrayMultiTreeNode);

            assertFalse(result);
        }

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2,level_1_3"})
        @DisplayName("当前节点为叶节点")
        void dropSubtree4(ArgumentsAccessor args) {
            List<ArrayMultiTreeNode<String>> arrayMultiTreeNodeList = addSubtrees(treeRoot, args);

            boolean result = arrayMultiTreeNodeList.get(0).dropSubtree(treeRoot);

            assertFalse(result);
        }

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2,level_1_3"})
        @DisplayName("删除不存在的非根,非叶,非null节点")
        void dropSubtree5(ArgumentsAccessor args) {
            addSubtrees(treeRoot, args);

            ArrayMultiTreeNode<String> newArrayMultiTreeNodeList = getLeafNode(args);

            boolean result = treeRoot.dropSubtree(newArrayMultiTreeNodeList);

            assertFalse(result);
        }
    }

    @Nested
    @DisplayName("hasSubtree方法测试")
    class HasSubTreeTest {

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2,level_1_3"})
        @DisplayName("该节点包含的节点")
        void hasSubtree1(ArgumentsAccessor args) {
            List<ArrayMultiTreeNode<String>> arrayMultiTreeNodeList = addSubtrees(treeRoot, args);

            boolean result = treeRoot.hasSubtree(arrayMultiTreeNodeList.get(1));
            assertTrue(result);
        }

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2,level_1_3"})
        @DisplayName("传入节点为null")
        void hasSubtree2(ArgumentsAccessor args) {
            addSubtrees(treeRoot, args);

            boolean result = treeRoot.hasSubtree(null);
            assertFalse(result);
        }

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2,level_1_3"})
        @DisplayName("传入节点为根节点")
        void hasSubtree3(ArgumentsAccessor args) {
            addSubtrees(treeRoot, args);

            boolean result = treeRoot.hasSubtree(new ArrayMultiTreeNode<>("newRoot"));
            assertFalse(result);
        }

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2,level_1_3"})
        @DisplayName("当前节点为叶节点")
        void hasSubtree4(ArgumentsAccessor args) {
            List<ArrayMultiTreeNode<String>> arrayMultiTreeNodeList = addSubtrees(treeRoot, args);

            boolean result = arrayMultiTreeNodeList.get(0).hasSubtree(treeRoot);
            assertFalse(result);
        }

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2,level_1_3"})
        @DisplayName("传入的节点为非空,非null,当前节点非叶的不存在节点")
        void hasSubtree5(ArgumentsAccessor args) {
            addSubtrees(treeRoot, args);

            ArrayMultiTreeNode<String> newArrayMultiTreeNodeList = getLeafNode(args);

            boolean result = treeRoot.hasSubtree(newArrayMultiTreeNodeList);

            assertFalse(result);
        }
    }

    @Nested
    @DisplayName("height方法测试")
    class HeightTest {

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2,level_1_3"})
        @DisplayName("查询非叶节点高度")
        void height1(ArgumentsAccessor args) {
            List<ArrayMultiTreeNode<String>> arrayMultiTreeNodeList = addSubtrees(treeRoot, args);

            int height = treeRoot.height();
            assertEquals(1, height);
        }

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2,level_1_3"})
        @DisplayName("查询叶节点高度")
        void height2(ArgumentsAccessor args) {
            List<ArrayMultiTreeNode<String>> arrayMultiTreeNodeList = addSubtrees(treeRoot, args);

            int height = arrayMultiTreeNodeList.get(0).height();
            assertEquals(0, height);
        }

    }

    @Nested
    @DisplayName("isLeaf方法测试")
    class IsLeafTest {

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2,level_1_3"})
        @DisplayName("查询非叶节点")
        void isLeaf1(ArgumentsAccessor args) {
            List<ArrayMultiTreeNode<String>> arrayMultiTreeNodeList = addSubtrees(treeRoot, args);

            boolean result = treeRoot.isLeaf();
            assertFalse(result);
        }

        @Test
        @DisplayName("查询叶节点")
        void isLeaf2() {
            boolean result = treeRoot.isLeaf();
            assertTrue(result);
        }
    }

    @Nested
    @DisplayName("remove方法测试")
    class RemoveTest {
        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2,level_1_3"})
        @DisplayName("删除存在节点")
        void remove1(ArgumentsAccessor args) {
            List<ArrayMultiTreeNode<String>> arrayMultiTreeNodeList = addSubtrees(treeRoot, args);

            boolean result = treeRoot.remove(arrayMultiTreeNodeList.get(0));
            assertTrue(result);
        }

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2,level_1_3"})
        @DisplayName("删除 null 节点")
        void remove2(ArgumentsAccessor args) {
            addSubtrees(treeRoot, args);

            boolean result = treeRoot.remove(null);
            assertFalse(result);
        }

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2,level_1_3"})
        @DisplayName("删除根节点")
        void remove3(ArgumentsAccessor args) {
            List<ArrayMultiTreeNode<String>> arrayMultiTreeNodeList = addSubtrees(treeRoot, args);

            ArrayMultiTreeNode<String> newRoot = new ArrayMultiTreeNode<>("newRoot");
            boolean result = treeRoot.remove(newRoot);
            assertFalse(result);
        }

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2,level_1_3"})
        @DisplayName("当前节点为叶节点")
        void remove4(ArgumentsAccessor args) {
            List<ArrayMultiTreeNode<String>> arrayMultiTreeNodeList = addSubtrees(treeRoot, args);

            boolean result = arrayMultiTreeNodeList.get(0).remove(treeRoot);
            assertFalse(result);
        }

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2,level_1_3"})
        @DisplayName("删除一个不存在的非叶,非根,非null节点")
        void remove5(ArgumentsAccessor args) {
            List<ArrayMultiTreeNode<String>> arrayMultiTreeNodeList = addSubtrees(treeRoot, args);
            ArrayMultiTreeNode<String> newNode = getLeafNode(args);

            boolean result = treeRoot.remove(newNode);

            assertFalse(result);
        }

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2,level_1_3"})
        @DisplayName("删除一个在多级子树中存在的节点")
        void remove6(ArgumentsAccessor args) {
            List<ArrayMultiTreeNode<String>> arrayMultiTreeNodeList = addSubtrees(treeRoot, args);
            List<ArrayMultiTreeNode<String>> subArrayMultiTreeNodeList = addSubtrees(arrayMultiTreeNodeList.get(0), args);

            boolean result = treeRoot.remove(subArrayMultiTreeNodeList.get(0));

            assertTrue(result);
        }
    }

    @Nested
    @DisplayName("siblings方法测试")
    class SiblingsTest {

        @Test
        @DisplayName("当前节点为根节点,对于异常进行审核")
        void siblings1() {
            Throwable TNExcp = assertThrows(TreeNodeException.class, () -> {
                treeRoot.siblings();
            });
            System.out.println(TNExcp.getMessage());

            assertEquals(String.format("Unable to find the siblings. The tree node %1$s is root", treeRoot), TNExcp.getMessage());
        }

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2,level_1_3"})
        @DisplayName("查询非根节点")
        void siblings2(ArgumentsAccessor args) {
            List<ArrayMultiTreeNode<String>> arrayMultiTreeNodeList = addSubtrees(treeRoot, args);

            Collection<ArrayMultiTreeNode<String>> sibArrayMultiTreeNodeList = (Collection<ArrayMultiTreeNode<String>>) arrayMultiTreeNodeList.get(0).siblings();
            System.out.println(sibArrayMultiTreeNodeList);
        }

        @ParameterizedTest
        @CsvSource({"level_1_1"})
        @DisplayName("父节点只有其一个节点")
        void siblings3(ArgumentsAccessor args) {
            List<ArrayMultiTreeNode<String>> arrayMultiTreeNodeList = addSubtrees(treeRoot, args);

            Collection<ArrayMultiTreeNode<String>> sibArrayMultiTreeNodeList = (Collection<ArrayMultiTreeNode<String>>) arrayMultiTreeNodeList.get(0).siblings();
            System.out.println(sibArrayMultiTreeNodeList);
        }
    }

    @Nested
    @DisplayName("subtrees方法测试")
    class SubtreesTest {

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2,level_1_3"})
        @DisplayName("查询根节点")
        void subtree1(ArgumentsAccessor args) {
            addSubtrees(treeRoot, args);

            treeRoot.subtrees();
        }

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2,level_1_3"})
        @DisplayName("查询叶节点")
        void subtree2(ArgumentsAccessor args) {
            List<ArrayMultiTreeNode<String>> arrayMultiTreeNodeList = addSubtrees(treeRoot, args);

            arrayMultiTreeNodeList.get(0).subtrees();
        }
    }

    @Nested
    @DisplayName("iterator方法测试")
    class IteratorTest {

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2"})
        @DisplayName("测试iterator成功的情况")
        void iterator1(ArgumentsAccessor args) {
            List<ArrayMultiTreeNode<String>> arrayMultiTreeNodeList = addSubtrees(treeRoot, args);

            ArrayMultiTreeNode<String>.TreeNodeIterator iterator = treeRoot.iterator();
            System.out.println(iterator);

            TreeNode<String> leftMostNode = iterator.leftMostNode();
            System.out.println(leftMostNode);

            ArrayMultiTreeNode<String>.TreeNodeIterator iterator2 = arrayMultiTreeNodeList.get(0).iterator();
            System.out.println(iterator2);

            TreeNode<String> rightSiblingNode = iterator2.rightSiblingNode();
            System.out.println(rightSiblingNode);
        }

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2"})
        @DisplayName("测试leftMostNode方法的异常情况")
        void iterator2(ArgumentsAccessor args) {
            ArrayMultiTreeNode<String> leafNode = getLeafNode(args);
            ArrayMultiTreeNode<String>.TreeNodeIterator iterator = leafNode.iterator();

            assertTrue(leafNode.isLeaf());

            // 当前节点为叶节点但是未报出异常,与注释不符
            assertThrows(NullPointerException.class, () -> {
                iterator.leftMostNode();
            });
        }

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2"})
        @DisplayName("测试rightSiblingNode方法的异常情况")
        void iterator3(ArgumentsAccessor args) {
            addSubtrees(treeRoot, args);

            ArrayMultiTreeNode<String>.TreeNodeIterator iterator = treeRoot.iterator();
            System.out.println(iterator);

            assertThrows(NullPointerException.class, () -> {
                iterator.rightSiblingNode();
            });
        }

    }

    @Nested
    @DisplayName("测试限制容量的构造函数")
    class ArrayMultiTreeNodeCapacityTest {
        private ArrayMultiTreeNode<String> treeRoot;

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2,level_1_3,level_1_4"})
        @DisplayName("测试添加的子树个数超出容量限制")
        void addSubtrees1(ArgumentsAccessor args) {
            treeRoot = new ArrayMultiTreeNode<>("root", 2);
            List<ArrayMultiTreeNode<String>> arrayMultiTreeNodeList = new ArrayList<>();
            for (int i = 0; i < args.size(); i++) {
                arrayMultiTreeNodeList.add(new ArrayMultiTreeNode<>(args.getString(i)));
            }
            treeRoot.addSubtrees(arrayMultiTreeNodeList);
        }

        @Test
        @DisplayName("测试构造函数中传入负数")
        void addSubtrees2() {
            assertThrows(IllegalArgumentException.class, () -> treeRoot = new ArrayMultiTreeNode<>("root", -2));
        }

        @Disabled
        @Test
        @DisplayName("测试最大容量")
        void addSubtrees3() {
            // 该部分无法测试,运行速度过慢,同时内存空间不足
            treeRoot = new ArrayMultiTreeNode<>("root");
            for (int i = 0; i < Integer.MAX_VALUE; i++) {
                treeRoot.add(new ArrayMultiTreeNode<>("1"));
            }
            System.out.println(treeRoot);
        }
    }


    @Nested
    @DisplayName("traversePreOrder方法测试")
    class TraversePreOrderTest {

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2"})
        @DisplayName("测试traversePreOrder")
        void traversePreOrder1(ArgumentsAccessor args) {
            addSubtrees(treeRoot, args);
            Collection<TreeNode<String>> mPreOrdered = new ArrayList<>();
            TraversalAction<TreeNode<String>> action = populateAction(mPreOrdered);
            treeRoot.traversePreOrder(action);
        }

    }

    @Nested
    @DisplayName("traversePostOrder方法测试")
    class TraversePostOrderTest {

        @ParameterizedTest
        @CsvSource({"level_1_1,level_1_2"})
        @DisplayName("测试traversePostOrder")
        void traversePostOrder1(ArgumentsAccessor args) {
            addSubtrees(treeRoot, args);
            Collection<TreeNode<String>> mPreOrdered = new ArrayList<>();
            TraversalAction<TreeNode<String>> action = populateAction(mPreOrdered);
            treeRoot.traversePostOrder(action);
        }

    }

    /**
     * Populates the input collection with the tree nodes, while traversing the tree
     * <p>
     * copy from TreeNode
     *
     * @param collection input collection to populate
     * @param <T>        type of the tree node
     * @return traversal action, which populates the input collection with the tree nodes
     */
    protected static <T> TraversalAction<TreeNode<T>> populateAction(final Collection<TreeNode<T>> collection) {
        return new TraversalAction<TreeNode<T>>() {
            @Override
            public void perform(TreeNode<T> node) {
                collection.add(node);
            }

            @Override
            public boolean isCompleted() {
                return false;
            }
        };
    }


    /**
     * 获得一个叶节点
     *
     * @param args 传入的csv文件参数
     * @return 叶节点
     */
    private ArrayMultiTreeNode<String> getLeafNode(ArgumentsAccessor args) {
        ArrayMultiTreeNode<String> arrayMultiTreeNode = new ArrayMultiTreeNode<>("newRoot");
        List<ArrayMultiTreeNode<String>> newArrayMultiTreeNodeList = addSubtrees(arrayMultiTreeNode, args);
        return newArrayMultiTreeNodeList.get(0);
    }


    /**
     * 将args中的数据添加到treeRoot的子树中
     *
     * @param args 传入的csv文件参数
     * @return 生成的数组对象
     */
    private List<ArrayMultiTreeNode<String>> addSubtrees(ArrayMultiTreeNode<String> arrayMultiTreeNode, ArgumentsAccessor args) {
        List<ArrayMultiTreeNode<String>> arrayMultiTreeNodeCollection = new ArrayList<>();
        for (int i = 0; i < args.size(); i++) {
            arrayMultiTreeNodeCollection.add(new ArrayMultiTreeNode<>(args.getString(i)));
        }
        arrayMultiTreeNode.addSubtrees(arrayMultiTreeNodeCollection);
        return arrayMultiTreeNodeCollection;
    }
}